home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / X11 / wais / ir / ir.c < prev    next >
C/C++ Source or Header  |  1995-05-09  |  40KB  |  1,339 lines

  1. /* WIDE AREA INFORMATION SERVER SOFTWARE:
  2.    No guarantees or restrictions.  See the readme file for the full standard
  3.    disclaimer.    
  4.   
  5. */
  6.  
  7. /* Change log:
  8.  * $Log:    ir.c,v $
  9.  * Revision 1.49  92/05/10  14:43:35  jonathan
  10.  * Made a little safer on NULL docid's when parsing.
  11.  * 
  12.  * Revision 1.48  92/05/05  14:56:33  jonathan
  13.  * Added definition of S_ISDIR for Mach and NeXT.  Fixed isdoctype macro to
  14.  * check for NULLs.
  15.  * 
  16.  * Revision 1.47  92/05/04  11:28:26  jonathan
  17.  * Changed logging result list to use log_level a little better.
  18.  * 
  19.  * Revision 1.46  92/04/30  12:24:45  jonathan
  20.  * split =* for ULTRIX CC.
  21.  * changed a couple of s_free's to free's for ULTRIX CC too.
  22.  * 
  23.  * Revision 1.45  92/04/01  17:08:14  jonathan
  24.  * Added code to handle FTP-like searches.
  25.  * 
  26.  * Revision 1.44  92/03/23  13:25:22  shen
  27.  * only print out the number of the results but not the message in the log
  28.  * 
  29.  * Revision 1.43  92/03/18  08:55:45  jonathan
  30.  * Removed databaseName argument to getDocumentText and getData.
  31.  * 
  32.  * Revision 1.42  92/03/05  07:07:20  shen
  33.  * add two more dummy arguments to call to init_search_engine
  34.  * 
  35.  * Revision 1.41  92/02/23  10:37:53  jonathan
  36.  * enforced limit on results in handleRelevanceFeedbackSearch (particularly
  37.  * for handline help queries).
  38.  * 
  39.  * Revision 1.40  92/02/23  09:57:57  jonathan
  40.  * Prevent return of help messages if query is empty but there are relevant
  41.  * documents.
  42.  * 
  43.  * Revision 1.39  92/02/21  11:07:25  jonathan
  44.  * Added RCSIdent.
  45.  * 
  46.  * Revision 1.38  92/02/21  11:00:10  jonathan
  47.  * Changed logging of init message to WLOG_MEDIUM.
  48.  * 
  49.  * Revision 1.37  92/02/19  16:56:48  jonathan
  50.  * mucked a bit with the no-results case.
  51.  * 
  52.  * Revision 1.36  92/02/19  13:55:22  jonathan
  53.  * Return catalog as result if no hits to search.
  54.  * Plug some more memory leaks.
  55.  * 
  56.  * Revision 1.35  92/02/19  10:39:27  jonathan
  57.  * Off by one in last fix (headerNum>=0).  Fixed it.
  58.  * 
  59.  * Revision 1.34  92/02/19  10:16:04  jonathan
  60.  * Added code to handle too many headers for buffer.  Reduces the number of
  61.  * headers until they fit, and marks the log with a warning.
  62.  * 
  63.  * Revision 1.33  92/02/17  12:40:04  jonathan
  64.  * Return catalog as well as source description for help queries.
  65.  * 
  66.  * 
  67.  * Revision 1.32  92/02/12  13:20:49  jonathan
  68.  * Added "$Log" so RCS will put the log message in the header
  69.  * 
  70. */
  71.  
  72. #ifndef lint
  73. static char *RCSid = "$Header: /tmp_mnt/net/quake/proj/wais/wais-8-b5/ir/RCS/ir.c,v 1.49 92/05/10 14:43:35 jonathan Exp $";
  74. #endif
  75.  
  76. /*----------------------------------------------------------------------*/
  77. /* This code implements a simple Z39.50+WAIS server, which consults a 
  78.    local database using Brewster's search engine.  The main routine is
  79.    interpret_buffer() which reads the contents of a receive buffer, and 
  80.    writes results back to a send buffer.
  81.  
  82. The basic structure is:
  83.  
  84. interpret_buffer gets bytes and returns bytes from whatever transport 
  85.    mechanism. It calls either handleInit (which handles init requests)
  86.    or handleSearch.
  87.  
  88. handleSearch calls either handleRelevanceFeedbackSearch or 
  89.    handleElementRetrieval based on the type of question.
  90.  
  91. handleElementRetrieval calls getData or getDocumentText to answer that 
  92.    question.
  93.  
  94. handleRelevanceFeedbackSearch calls run_search and/or help_header to answer 
  95.    the question.
  96.  
  97.  
  98. A server must supply:
  99.   getData, getDocumentText, run_search, and help_header
  100. then it should work.
  101.  
  102.    To do:
  103.    - help facilities:
  104.      on a null query pass back random documents from the pool (?)
  105.    - Add dates to search responses
  106.  */
  107.  
  108. /* change log:
  109.  *  3/91 fixed db name defaulting for info server
  110.  *  5/31/91 fixed handleRelevanceFeedbackSearch to do search if
  111.  *          no seedwords but relevant document supplied - JG
  112.  *  5/31/91 fixed databaseName in handleElementRetrieval - HWM
  113.  *  7/19/91 fixed handleElementRetrieval prototype -BK
  114.  */
  115. /*----------------------------------------------------------------------*/
  116.  
  117. #include "server.h"
  118. #include "ir.h"
  119. #include "wprot.h"
  120. #include "irsearch.h"
  121. #include "docid.h"
  122. #include "cutil.h"
  123. #include "irfiles.h" /* for pathname_name */
  124. #include "irretrvl.h"
  125. #include "sockets.h"  /* for connect_to_server */
  126. #include "panic.h"
  127.  
  128. #include <string.h>
  129. #include <ctype.h>
  130. #include <math.h>
  131.  
  132. #ifdef ANSI_LIKE
  133. #include <stdlib.h>
  134. #else
  135. #include "ustubs.h"
  136. #endif
  137.  
  138. #include <sys/stat.h>
  139. #include "irdirent.h"
  140.  
  141. /* forward declarations */
  142.  
  143. static boolean needs_help _AP ((char *question));
  144.  
  145. static WAISDocumentHeader *help_header _AP((char *database_name, 
  146.                         char *index_directory));
  147.  
  148. static WAISDocumentHeader *catalog_header _AP((char *database_name, 
  149.                            char *index_directory,
  150.                            boolean results));
  151.  
  152. static void handleInit _AP((char** recBuf,char** sendBuf,
  153.                 long* sendBufLen,
  154.                 long* maxBufLen));
  155.                        
  156. static void handleSearch _AP((char** recBuf,char** sendBuf,
  157.                   long* sendBufLen,
  158.                   long waisProtocolVersion,
  159.                   char *index_directory));
  160.  
  161. static void handleRelevanceFeedbackSearch _AP((SearchAPDU* search,
  162.                            char** sendBuf,long* sendBufLen,
  163.                            long waisProtocolVersion,
  164.                            char *index_directory));
  165.                                           
  166. static void handleElementRetrieval _AP((SearchAPDU* search,
  167.                     char** sendBuf,
  168.                     long* sendBufLen,
  169.                     long waisProtocolVersion,
  170.                     char *index_directory));
  171.                                    
  172. static void handleFTPSearch _AP((SearchAPDU* search,
  173.                  char** sendBuf,long* sendBufLen,
  174.                  long waisProtocolVersion,
  175.                  char *index_directory));
  176.  
  177. #define isdoctype(doc, doctype) \
  178.  (((doc) != NULL) && \
  179.  ((doc)->Type != NULL) && \
  180.  !strcmp((doc)->Type, doctype))
  181.  
  182. #ifdef Mach
  183. #include <sys/inode.h>
  184. #define S_ISDIR(f_mode) (f_mode & IFDIR)
  185. #endif /* Mach */
  186.  
  187. #if (defined(NeXT) && !(defined(S_ISDIR)))
  188. #define S_ISDIR(f_mode) ((fmode) & S_IFDIR)
  189. #endif
  190.  
  191. /*----------------------------------------------------------------------*/
  192. /* Utility */
  193.   
  194. /*----------------------------------------------------------------------*/
  195. /* note - at present, it is not clear to me what the value of present-status
  196.    is, and how it is to be interpreted.  Furthermore, are our text retrieval
  197.    queries considered presents, or are they searches?
  198.  */
  199.  
  200.  
  201. /*----------------------------------------------------------------------*/
  202.  
  203. /* interpret_buffer()
  204. char* receiveBuffer - buffer containing data to interpret
  205. long receiveBufLen - how much data is there
  206. char* sendBuffer - buffer to write results to
  207. long sendBufLen - how much space there is to write to
  208. long* maxBufferSize - see below
  209. long waisProtocolVersion - what version of the wias protocol is in use
  210. char *index_directory - the directory to find the indexes on a search
  211.  
  212. maxBufferSize is a pointer to a per connection variable that contains the
  213. maximum size send/receive buffer to use.  Seems a lot like sendBufLen
  214. does't it.  Well it usually is, but not always.
  215.  
  216. Here is how it works from a server's point of view.  
  217.  
  218. When the client connection is first established, the server spawns a new
  219. process to deal with it.  The new process contains a global variable
  220. (bufferSize in server.c) which is initialized to BUFSZ (defined in server.h).
  221. This is the physical size of the server's internal bufferes.
  222. Clearly that is the absolute maximum size of any z3950 message to or from
  223. this server.
  224.  
  225. So now *maxBufferSize = sendBufLen.  
  226.  
  227. Now, the first thing that a z3950 client is likely to do is send an init
  228. APDU.  The only useful thing (and it is useful) that the init APDU
  229. currently does is allow the client and server to negotiate the maxumum size
  230. of the messages that they will send.  This takes place somewhere down
  231. inside of the interpret_buffer() logic where the APDU's are decoded and
  232. response APDU's are recoded.  A pointer to bufferSize is passed to
  233. interpret_buffer() in the maxBufferSize argument, and if the buffer happens
  234. to contain an init message, bufferSize is changed (for the rest of the
  235. connection).
  236.  
  237. That is the only function maxBufferSize serves.  Note that I could have
  238. gotten rid of sendBufLen, and just used *maxBufferSize, but sendBufLen can
  239. be and does get modified by the z3950 APDU writting code, and we don't want
  240. the overall value being modified.
  241.  
  242. */
  243.  
  244. long
  245. interpret_buffer(receiveBuffer,
  246.             receiveBufLen,
  247.             sendBuffer,
  248.             sendBufLen,
  249.             maxBufferSize,
  250.             waisProtocolVersion,
  251.             index_directory)
  252. char* receiveBuffer;
  253. long receiveBufLen;
  254. char* sendBuffer;
  255. long sendBufLen;
  256. long* maxBufferSize;
  257. long waisProtocolVersion;
  258. char *index_directory;
  259. /* read & interpret receiveBuffer until receiveBufLen.  Write results into
  260.    send buffer.  Return number of bytes written - negative if there was an 
  261.    error 
  262.  */
  263. {
  264.   char* readPos = receiveBuffer;
  265.   char* writePos = sendBuffer;
  266.  
  267.   while (readPos - receiveBuffer < receiveBufLen && /* there is more to read */
  268.          writePos != NULL    /* no write error */
  269.      )
  270.     { pdu_type pdu = peekPDUType(readPos);
  271.       switch (pdu)
  272.     { case initAPDU:
  273.         handleInit(&readPos,&writePos,&sendBufLen,maxBufferSize);
  274.         break;
  275.       case searchAPDU:
  276.         handleSearch(&readPos,&writePos,&sendBufLen,
  277.              waisProtocolVersion, index_directory);
  278.         break;
  279.       default:
  280.         /* unknown APDU error */
  281.         writePos = NULL;
  282.         waislog(WLOG_HIGH, WLOG_ERROR,
  283.             "Error in interpret_message: unknown APDU type.");
  284.         break;
  285.       }
  286.     }
  287.   
  288.   if(writePos == NULL) {
  289.     waislog(WLOG_HIGH, WLOG_ERROR,
  290.         "Error in interpret_message: NULL writePos.");
  291.     return (0);
  292.   }
  293.   else return(writePos - sendBuffer);
  294. }
  295.  
  296. /*----------------------------------------------------------------------*/
  297.  
  298. static void handleInit _AP((char** recBuf,char** sendBuf,
  299.                 long* sendBufLen,long* maxBufferSize));
  300.  
  301. static void
  302. handleInit(recBuf,sendBuf,sendBufLen,maxBufferSize)
  303. char** recBuf;
  304. char** sendBuf;
  305. long* sendBufLen;
  306. long* maxBufferSize;
  307. /* negotiate functionality and buffer sizes.  A session ususally begins
  308.    with one of these, but is not required to.  
  309.    NOTE - even if the server decides not to accept the client, it does
  310.    not shut down the connection.  It simply declies acceptatance, and 
  311.    waits for the client to shut down.
  312.  */
  313. {
  314.   InitAPDU* anInit = NULL;
  315.   
  316.   /* read the init - note there is no WAIS protocol extension here */
  317.   *recBuf = readInitAPDU(&anInit,*recBuf);
  318.   
  319.   if (recBuf == NULL || *recBuf == NULL)
  320.     { *sendBuf = NULL;        /* error in the read */
  321.       return;
  322.     }
  323.   else                /* respond to the init */
  324.     { InitResponseAPDU* reply = NULL;
  325.       WAISInitResponse* wais_response = NULL;
  326.       boolean connectionAccepted;
  327.      
  328.       /* negotiate services */
  329.       if (anInit->willPresent == false &&
  330.       anInit->willDelete == false)
  331.     connectionAccepted = true;
  332.       else
  333.     connectionAccepted = false;
  334.        
  335.       /* negotiate buffer sizes */
  336.       if (*maxBufferSize > anInit->MaximumRecordSize)
  337.     *maxBufferSize = anInit->MaximumRecordSize;
  338.      
  339.       if(anInit->IDAuthentication != NULL)
  340.     waislog(WLOG_MEDIUM, WLOG_INFO, "Init message: %s", 
  341.         anInit->IDAuthentication);
  342.  
  343.       /* not much use huh? */
  344.       wais_response = makeWAISInitResponse(0L,0L,NULL,NULL,NULL,NULL); 
  345.      
  346.       reply = makeInitResponseAPDU(connectionAccepted,
  347.                    true,false,false,false,false,*maxBufferSize,
  348.                    *maxBufferSize,NULL,
  349.                    defaultImplementationID(),
  350.                    defaultImplementationName(),
  351.                    defaultImplementationVersion(),NULL,
  352.                    wais_response);
  353.  
  354.       /* write it */
  355.       *sendBuf = writeInitResponseAPDU(reply,*sendBuf,sendBufLen);
  356.      
  357.       /* free everything */
  358.       freeInitAPDU(anInit);
  359.       freeInitResponseAPDU(reply);
  360.       freeWAISInitResponse(wais_response);
  361.     }
  362. }
  363.  
  364.  
  365. /*----------------------------------------------------------------------*/
  366. static boolean
  367. isRemoteDB(db)
  368. char * db;
  369. {
  370.   return(strchr(db,'@') != NULL);
  371. }
  372.  
  373. /*----------------------------------------------------------------------*/
  374. #include "wmessage.h"
  375.  
  376. struct {
  377.   char host[256];
  378.   long port;
  379.   FILE *connection;
  380.   long buffer_size;
  381. } last_connection;
  382.  
  383. static void
  384. forwardSearch(aSearch,sendBuf,sendBufLen,waisProtocolVersion)
  385. SearchAPDU* aSearch;
  386. char** sendBuf;
  387. long* sendBufLen;
  388. long waisProtocolVersion;
  389. {
  390.   FILE *connection;
  391.   char hostname[1000], db[1000], *p, *p2;
  392.   long port, len, rlen;
  393.   char message[BUFSZ], response[BUFSZ];
  394.  
  395.   p = strchr(aSearch->DatabaseNames[0], '@');
  396.   strncpy(db, aSearch->DatabaseNames[0], p-aSearch->DatabaseNames[0]);
  397.   db[p-aSearch->DatabaseNames[0]] = 0;
  398.   p2 = strchr(p+1, ':');
  399.   if(p2 == NULL) {
  400.     strcpy(hostname, p+1);
  401.     port = 210;
  402.   }
  403.   else {
  404.     strncpy(hostname, p+1, p2-(p+1));
  405.     hostname[p2-(p+1)] = 0;
  406.     port = atoi(p2+1);
  407.   }
  408.  
  409.   strcpy(aSearch->DatabaseNames[0], db);
  410.   rlen = len = BUFSZ;
  411.   writeSearchAPDU(aSearch, message+HEADER_LENGTH, &len);
  412.   len = BUFSZ-len;
  413.   if(hostname[0] != 0) {
  414.     if(strcmp(hostname, last_connection.host) == 0 &&
  415.        port == last_connection.port)
  416.       connection = last_connection.connection;
  417.     else {
  418.       if (last_connection.connection != NULL)
  419.     close_connection(last_connection.connection);
  420.       strcpy(last_connection.host, hostname);
  421.       last_connection.port = port;
  422.       last_connection.connection = (FILE*)connect_to_server(hostname, port);
  423.       connection = last_connection.connection;
  424.       if(connection != NULL) {
  425.     char userInfo[500], hostname[80], init_message[1000];
  426.  
  427.     gethostname(hostname, 80);
  428. #ifdef TELL_USER
  429.     sprintf(userInfo, "server forwarding %s, from host: %s, user: %s",
  430.         VERSION, hostname, getenv("USER"));
  431. #else
  432.     sprintf(userInfo, "server forwarding %s, from host: %s", VERSION, hostname);
  433. #endif
  434.  
  435.     last_connection.buffer_size =
  436.       init_connection(init_message, response,
  437.               BUFSZ,
  438.               connection,
  439.               userInfo);
  440.       }
  441.     }
  442.     if(connection != NULL)
  443.       {
  444.     len = interpret_message(message, len,
  445.                 response, last_connection.buffer_size,
  446.                 connection, false);
  447.       }
  448.     else {
  449.       static diagnosticRecord* diags[2] = {NULL, NULL};
  450.       SearchResponseAPDU* response = NULL;
  451.       WAISSearchResponse* wais_response = NULL;
  452.       char message[255];
  453.  
  454.       sprintf(message, "Database not available: %s@%s:%d.",
  455.           db, last_connection.host, last_connection.port);
  456.       diags[0] = makeDiag(true,D_RecordNotAuthorizedToBeSent,
  457.               message);
  458.  
  459.       wais_response = makeWAISSearchResponse(NULL,NULL,NULL,
  460.                          NULL,NULL,NULL,NULL,diags);
  461.       response = makeSearchResponseAPDU(0L,0L,
  462.                     1L,
  463.                     0L,UNUSED,FAILURE,
  464.                     aSearch->ReferenceID, wais_response);
  465.       *sendBuf = writeSearchResponseAPDU(response,*sendBuf,sendBufLen);
  466.  
  467.       freeSearchResponseAPDU(response);
  468.       freeWAISSearchResponse(wais_response);
  469.       waislog(WLOG_HIGH, WLOG_ERROR, message);
  470.       return;
  471.     }
  472.   }
  473.   else
  474.     len = interpret_message(message, len,
  475.                 response, last_connection.buffer_size,
  476.                 NULL, false);
  477.   bcopy(response+HEADER_LENGTH, *sendBuf, len);
  478.   *sendBuf+=len;
  479. }
  480.  
  481. /*----------------------------------------------------------------------*/
  482.  
  483. static void
  484. handleSearch(recBuf,sendBuf,sendBufLen,waisProtocolVersion,index_directory)
  485. char** recBuf;
  486. char** sendBuf;
  487. long* sendBufLen;
  488. long waisProtocolVersion;
  489. char *index_directory;
  490. /* figure out what kind of search this is, (query or retrieval) and
  491.    dispatch to the appropriate function 
  492.  */
  493. {
  494.   SearchAPDU* aSearch = NULL;
  495.  
  496.   /* read the search data */
  497.   *recBuf = readSearchAPDU(&aSearch,*recBuf);
  498.  
  499.   if (*recBuf == NULL)
  500.     { *sendBuf = NULL;        /* error in the read */
  501.       return;
  502.     }
  503.   else
  504.     {                /* dispatch on the query type */
  505.       if((aSearch->DatabaseNames != NULL) &&
  506.      (aSearch->DatabaseNames[0] != NULL) &&
  507.      isRemoteDB(aSearch->DatabaseNames[0]))
  508.     forwardSearch(aSearch,sendBuf,sendBufLen,waisProtocolVersion);
  509.       else {
  510.     if (strcmp(aSearch->QueryType,QT_TextRetrievalQuery) == 0) {
  511.       handleElementRetrieval(aSearch,sendBuf,sendBufLen,
  512.                  waisProtocolVersion, index_directory);
  513.     }
  514.     else if (strcmp(aSearch->QueryType,QT_RelevanceFeedbackQuery) == 0) {
  515.       char *seeds, *s;
  516.  
  517.       s = seeds = s_strdup(((WAISSearch *)aSearch->Query)->SeedWords);
  518.       while(*s != 0) {
  519.         if(*s == '\n' || *s == '\r') *s = ' ';
  520.         s++;
  521.       }
  522.  
  523.       if(aSearch->DatabaseNames != NULL &&
  524.          aSearch->DatabaseNames[0] != NULL)
  525.         waislog(WLOG_LOW, WLOG_SEARCH,
  526.             "Search! Database: %s, Seed Words: %s", 
  527.             aSearch->DatabaseNames[0], 
  528.             seeds);
  529.       else
  530.         waislog(WLOG_LOW, WLOG_SEARCH, 
  531.             "Search! Database: None, Seed Words: %s", 
  532.             seeds);
  533.  
  534.       handleRelevanceFeedbackSearch(aSearch,sendBuf,sendBufLen,
  535.                     waisProtocolVersion,
  536.                     index_directory);
  537.     }
  538.     else {
  539.       waislog(WLOG_HIGH, WLOG_ERROR, "Unknown search type");
  540.       *sendBuf = NULL;    /* error - unknown search type */
  541.     }
  542.     fflush(stderr);
  543.       }
  544.     }
  545. }
  546.  
  547.  
  548.      
  549.  
  550. /*----------------------------------------------------------------------*/
  551.  
  552. static boolean needs_help(question)
  553. char *question;
  554. /* returns true if the user wants help */
  555. {
  556.   if(question[0] == '\0')  /* null question, must need help */
  557.     return(true);
  558.   if(question[0] == '?')
  559.     return(true);
  560.   if(question[0] == '*')
  561.     return(true);
  562.   if(strlen(question) < 20){
  563.     if((NULL != strstr(question, "help")) ||
  564.        (NULL != strstr(question, "HELP")) ||
  565.        (NULL != strstr(question, "Help")) ||
  566.        (NULL != strstr(question, "all"))){
  567.       return(true);
  568.     }      
  569.   }
  570.   return(false);
  571. }
  572.  
  573. /* returns a help header to be returned or NULL if not possible */
  574. static WAISDocumentHeader *help_header(database_name, index_directory)
  575.      char *database_name;
  576.      char *index_directory;
  577. {
  578.   /* make a help document */
  579.   hit help;
  580.   char local_id[MAX_FILENAME_LEN + 60];
  581.  
  582.   strncpy(help.filename,
  583.       merge_pathnames(database_name,index_directory), 
  584.       MAX_FILENAME_LEN);
  585.   strncat(help.filename, source_ext, MAX_FILENAME_LEN);
  586.   /* printf("help filename %s", help.filename); */
  587.  
  588.   strncpy(help.headline, "Information on database: ", MAX_FILENAME_LEN);
  589.   strncat(help.headline, pathname_name(database_name), 
  590.       MAX_FILENAME_LEN);
  591.   sprintf(local_id, "%ld %ld %s", 0L, 0L, help.filename);
  592.  
  593.   if(probe_file(help.filename))
  594.     { 
  595.       DocID* theDocID = NULL;
  596.       long length;
  597.       long lines;
  598.       char **type = NULL;
  599.  
  600.       help.start_character = 0;
  601.       help.end_character = 0;
  602.     
  603.       { FILE *stream = s_fopen(help.filename, "r");
  604.     lines = count_lines(stream);
  605.     length = file_length(stream);
  606.         s_fclose(stream);
  607.       }
  608.  
  609.       type = (char**)s_malloc((size_t)(sizeof(char*) * 2));
  610.       type[0] = s_strdup("WSRC");
  611.       type[1] = NULL;
  612.  
  613.       /* then there is a source structure to return */
  614.       theDocID = makeDocID();
  615.       theDocID->originalDatabase = stringToAny(database_name); /* XXX */
  616.       theDocID->originalLocalID = stringToAny(local_id);
  617.  
  618.       return(makeWAISDocumentHeader(anyFromDocID(theDocID),
  619.                     UNUSED,
  620.                     MAX_NORMAL_SCORE,
  621.                     UNUSED,
  622.                     length,lines,
  623.                     type,
  624.                     s_strdup(database_name), /* XXX */
  625.                     NULL, /* date */
  626.                     s_strdup(help.headline),
  627.                     NULL));
  628.     }    
  629.   else 
  630.     return(NULL);
  631. }
  632.  
  633. /* returns the catalog document to be returned or NULL if not possible */
  634.  
  635. static WAISDocumentHeader *catalog_header(database_name, index_directory, results)
  636.      char *database_name;
  637.      char *index_directory;
  638.      boolean results;
  639. {
  640.   /* make a help document */
  641.   hit catalog;
  642.   char local_id[MAX_FILENAME_LEN + 60];
  643.  
  644.   strncpy(catalog.filename,
  645.       merge_pathnames(database_name,index_directory), 
  646.       MAX_FILENAME_LEN);
  647.   strncat(catalog.filename, catalog_ext, MAX_FILENAME_LEN);
  648.   /* printf("catalog filename %s", catalog.filename); */
  649.  
  650.   if(results)
  651.     strncpy(catalog.headline,
  652.         "Catalog for database: ",
  653.         MAX_FILENAME_LEN);
  654.   else
  655.     strncpy(catalog.headline,
  656.         "Search produced no result. Here's the Catalog for database: ",
  657.         MAX_FILENAME_LEN);
  658.  
  659.   strncat(catalog.headline, pathname_name(database_name), 
  660.       MAX_FILENAME_LEN);
  661.   sprintf(local_id, "%ld %ld %s", 0L, 0L, catalog.filename);
  662.  
  663.   if(probe_file(catalog.filename))
  664.     { 
  665.       DocID* theDocID = NULL;
  666.       long length;
  667.       long lines;
  668.       char **type = NULL;
  669.  
  670.       catalog.start_character = 0;
  671.       catalog.end_character = 0;
  672.     
  673.       { FILE *stream = s_fopen(catalog.filename, "r");
  674.     lines = count_lines(stream);
  675.     length = file_length(stream);
  676.         s_fclose(stream);
  677.       }
  678.  
  679.       type = (char**)s_malloc((size_t)(sizeof(char*) * 2));
  680.       type[0] = s_strdup("TEXT");
  681.       type[1] = NULL;
  682.  
  683.       /* then there is a catalog structure to return */
  684.       theDocID = makeDocID();
  685.       theDocID->originalDatabase = stringToAny(database_name); /* XXX */
  686.       theDocID->originalLocalID = stringToAny(local_id);
  687.  
  688.       return(makeWAISDocumentHeader(anyFromDocID(theDocID),
  689.                     UNUSED,
  690.                     (results ? MAX_NORMAL_SCORE:0),
  691.                     UNUSED,
  692.                     length,lines,
  693.                     type,
  694.                     s_strdup(database_name), /* XXX */
  695.                     NULL, /* date */
  696.                     s_strdup(catalog.headline),
  697.                     NULL));
  698.     }    
  699.   else 
  700.     return(NULL);
  701. }
  702.  
  703. /* picks a set of random documents from the database 
  704. static void pick_random_documents(aSearch, headers, &headerNum)
  705. {
  706.   
  707. }
  708. */
  709.  
  710.  
  711. /*----------------------------------------------------------------------*/
  712.  
  713. static void
  714. handleRelevanceFeedbackSearch(aSearch,sendBuf,sendBufLen,
  715.                   waisProtocolVersion,
  716.                   index_directory)
  717. SearchAPDU* aSearch;
  718. char** sendBuf;
  719. long* sendBufLen;
  720. long waisProtocolVersion;
  721. char *index_directory;
  722.   DocObj *doc = NULL;
  723.   SearchResponseAPDU* response = NULL;
  724.   WAISSearchResponse* wais_response = NULL;
  725.  
  726.   WAISDocumentHeader** headers = NULL;
  727.   long headerNum = 0;
  728.   long max_headers = ((WAISSearch *)aSearch->Query)->MaxDocumentsRetrieved;
  729.   char* seedwords_used = NULL;
  730.   diagnosticRecord** diags = NULL;
  731.   char *seed_words_used = s_strdup(((WAISSearch *)aSearch->Query)->SeedWords);
  732.   boolean search_status;
  733.  
  734.   if(((WAISSearch *)aSearch->Query)->Docs!=NULL &&
  735.      (doc=((WAISSearch *)aSearch->Query)->Docs[0]) != NULL &&
  736.      (isdoctype(doc, "TEXT-FTP") || isdoctype(doc, "FTP-DIR"))) {
  737.     handleFTPSearch(aSearch,sendBuf,sendBufLen, waisProtocolVersion, index_directory);
  738.     return;
  739.   }
  740.  
  741.   /* construct a response list */
  742.   headers = (WAISDocumentHeader**)
  743.     s_malloc((size_t)
  744.          (sizeof(WAISDocumentHeader*) * 
  745.           (1 + max_headers)));
  746.   headers[0] = NULL;
  747.  
  748.   if(0 != init_search_engine(index_directory, false, true, 0, 0, 0))
  749.     panic("unable to initialize search engine");
  750.   
  751.   /* handle help queries */
  752.   if(seed_words_used[0] == '\0' &&
  753.      ((WAISSearch *)aSearch->Query)->Docs != NULL) {
  754.     1;
  755.   }
  756.   else if(needs_help(seed_words_used)) {
  757.       WAISDocumentHeader *header;
  758.       char *database_name = (aSearch->DatabaseNames == NULL) ?
  759.       INFO_DATABASE_NAME : aSearch->DatabaseNames[0];
  760.  
  761.     if(headerNum < max_headers) {
  762.       header = help_header(database_name, index_directory);
  763.       if(NULL != header) {
  764.     headers[headerNum++] = header;
  765.     headers[headerNum] = NULL;    
  766.     ((WAISSearch *)aSearch->Query)->MaxDocumentsRetrieved--;
  767.       }
  768.     }
  769.     if(headerNum < max_headers) {
  770.       header = catalog_header(database_name, index_directory, true);
  771.       if(NULL != header){
  772.     headers[headerNum++] = header;
  773.     headers[headerNum] = NULL;    
  774.     ((WAISSearch *)aSearch->Query)->MaxDocumentsRetrieved--;
  775.       }
  776.     }
  777.   }
  778.  
  779.   if(seed_words_used[0] == '\0' &&
  780.      ((WAISSearch *)aSearch->Query)->Docs == NULL)
  781.    {
  782.      /* pick_random_documents(aSearch, headers, &headerNum); */
  783.      search_status = true;
  784.    }
  785.   else
  786.    { /* run the search on the database.  If a new
  787.     search engine were to be used, this is where it would be hooked in */
  788.      search_status = run_search(aSearch, headers,&diags, index_directory, 
  789.                 &seed_words_used, waisProtocolVersion,
  790.                 &headerNum);
  791.    }
  792.  
  793. #define CATALOG_FOR_NO_RESULTS
  794. #ifdef CATALOG_FOR_NO_RESULTS
  795.   if(headerNum == 0 && headerNum < max_headers) {
  796.     char *database_name = (aSearch->DatabaseNames == NULL) ?
  797.       INFO_DATABASE_NAME : aSearch->DatabaseNames[0];
  798.     WAISDocumentHeader *header = catalog_header(database_name, index_directory, false);
  799.  
  800.     if(NULL != header){
  801.       waislog(WLOG_MEDIUM, WLOG_INFO,
  802.           "Search had no hits, returning catalog");
  803.       headers[headerNum++] = header;
  804.       headers[headerNum] = NULL;    
  805.     }
  806.   }
  807. #endif /* CATALOG_FOR_NO_RESULTS */
  808.  
  809.   wais_response = makeWAISSearchResponse(seedwords_used,headers,NULL,
  810.                                          NULL,NULL,NULL,NULL,diags);
  811.   response = makeSearchResponseAPDU(search_status,0L,
  812.                     headerNum + ((diags == NULL) ? 0 : 1),
  813.                     0L,UNUSED,SUCCESS,
  814.                                     aSearch->ReferenceID, wais_response);
  815.   /* write it */
  816.   {
  817.     char *buff;
  818.     long len;
  819.     boolean it_fit = true;
  820.  
  821.     while(headerNum >= 0) {
  822.       buff = *sendBuf;
  823.       len = *sendBufLen;
  824.       if ((buff = writeSearchResponseAPDU(response,buff,&len)) == NULL) {
  825.  
  826.     it_fit = false; /* didn't make it in the buffer. */
  827.     headerNum--; 
  828.     s_free(headers[headerNum]);
  829.     headers[headerNum] = NULL;
  830.  
  831.     s_free(wais_response);
  832.     wais_response = makeWAISSearchResponse(seedwords_used,headers,NULL,
  833.                            NULL,NULL,NULL,NULL,diags);
  834.     
  835.     freeSearchResponseAPDU(response);
  836.     response = makeSearchResponseAPDU(search_status,0L,
  837.                       headerNum + ((diags == NULL) ? 0:1),
  838.                       0L,UNUSED,SUCCESS,
  839.                       aSearch->ReferenceID, wais_response);
  840.       }
  841.       else {
  842.     break;
  843.       }
  844.     }
  845.     *sendBuf = buff;
  846.     *sendBufLen = len;
  847.     if (!it_fit) {
  848.       waislog(WLOG_HIGH, WLOG_WARNING, 
  849.           "Buffer overflow, adjusted results from %ld", 
  850.           ((WAISSearch *)aSearch->Query)->MaxDocumentsRetrieved);
  851.     }
  852.   }
  853.  
  854.   { /* generate report on results. */
  855.     char *message;
  856.     long size, i;
  857.  
  858.     /* calculate total length needed for log report */
  859.     for(size = 0L, i = 0; i < headerNum; i++) 
  860.       size+=(headers[i]->DocumentID->size+2);
  861.     if (size > 0) {
  862.       message = s_malloc(size);
  863.       message[0] = 0;
  864.  
  865.       for (i = 0; i < headerNum; i++) {
  866.     char docname[MAX_FILE_NAME_LEN+50];
  867.     DocID *docid = docIDFromAny(headers[i]->DocumentID);
  868.     char *docidstring = anyToString(GetLocalID(docid));
  869.  
  870.     sprintf(docname, "%s", docidstring);
  871.     s_strncat(message, docname, headers[i]->DocumentID->size, size);
  872.  
  873.     s_free(docid); s_free(docidstring);
  874.  
  875.     if ( i < headerNum-1)
  876.       strcat(message, ", ");
  877.       }
  878.       waislog(WLOG_MEDIUM, WLOG_RESULTS, "Returned %d results", headerNum);
  879.       waislog(WLOG_LOW, WLOG_RESULTS, "Results: %s", message);
  880.       s_free(message);
  881.     }
  882.     else
  883.       waislog(WLOG_LOW, WLOG_RESULTS,
  884.           "Returned 0 results.  Aww.");
  885.   }
  886.   freeWAISSearch((WAISSearch*)aSearch->Query); 
  887.   freeSearchAPDU(aSearch);
  888.   freeSearchResponseAPDU(response);
  889.   freeWAISSearchResponse(wais_response); /* free headers & seed_words_used */
  890. }
  891.  
  892. /*----------------------------------------------------------------------*/
  893.  
  894. static void 
  895. handleElementRetrieval(aSearch,sendBuf,sendBufLen,waisProtocolVersion, index_directory)
  896. SearchAPDU* aSearch;
  897. char** sendBuf;
  898. long* sendBufLen;
  899. long waisProtocolVersion;
  900. char *index_directory;
  901. /* this is a type 1 query of the restricted form specified in the 
  902.    WAIS-protocol.  Interpret it and write out an appropriate search
  903.    response. (note the valid element sets are Document-Text,Document-Headlines,
  904.    and Document-Codes but we only support text for now).
  905.  */
  906.   SearchResponseAPDU* response = NULL;
  907.   WAISSearchResponse* wais_response = NULL;
  908.   DocObj** docs = NULL;
  909.   DocObj* doc = NULL;
  910.   char *databaseName;
  911.   void **elementList = NULL;
  912.   void *element = NULL;
  913.   diagnosticRecord** diags = NULL;
  914.   diagnosticRecord* diag = NULL;
  915.   long numDiags = 0L;
  916.   long numElements = 0L;
  917.   long i;
  918.   database* db;
  919.   char* new_db_name;
  920.   
  921.   /* read the query */
  922.   docs = readWAISTextQuery((any*)aSearch->Query);
  923.   databaseName = (aSearch->DatabaseNames == NULL) ?
  924.     INFO_DATABASE_NAME : aSearch->DatabaseNames[0];
  925.  
  926.   new_db_name = merge_pathnames(databaseName, index_directory);
  927.  
  928.   /* assemble the elements and construct a response */
  929.   for (i = 0L, doc = docs[i]; doc != NULL; doc = docs[++i])
  930.    { 
  931.      long errorCode;
  932.      any* bufAny;
  933.      long size;
  934.  
  935.      if (doc->Type != NULL &&
  936.      strcmp(doc->Type, "WAIS_NEXT") == 0) {
  937.        char docname[MAX_FILE_NAME_LEN+50], *buffer;
  938.  
  939.        db = openDatabase(new_db_name, false, true);
  940.        if ((size = 
  941.         next_doc(docname, 
  942.              anyToString(GetLocalID(docIDFromAny(doc->DocumentID))),
  943.              db))
  944.        > 0) {
  945.      buffer = s_malloc(strlen(docname)+50);
  946.      sprintf(buffer, "%s, %d", docname, size);
  947.      bufAny = makeAny(strlen(buffer)+1,buffer);
  948.      element = (void*)makeWAISDocumentText(duplicateAny(doc->DocumentID),0L,bufAny);
  949.        }
  950.        else element = NULL;
  951.        closeDatabase(db);
  952.      }
  953.      else if (doc->Type != NULL &&
  954.           strcmp(doc->Type, "WAIS_PREV") == 0) {
  955.        char docname[MAX_FILE_NAME_LEN+50], *buffer;
  956.        any* bufAny;
  957.        long size;
  958.  
  959.        db = openDatabase(new_db_name, false, true);
  960.        if ((size = 
  961.         previous_doc(docname, 
  962.              anyToString(GetLocalID(docIDFromAny(doc->DocumentID))),
  963.              db))
  964.        > 0) {
  965.      buffer = s_malloc(strlen(docname)+50);
  966.      sprintf(buffer, "%s, %d", docname, size);
  967.      bufAny = makeAny(strlen(buffer),buffer);
  968.      element = (void*)makeWAISDocumentText(duplicateAny(doc->DocumentID),0L,bufAny);
  969.        }
  970.        else element = NULL;
  971.        closeDatabase(db);
  972.      }
  973.      else if (doc->ChunkCode == CT_line)
  974.        element = (void*)getDocumentText(doc, &errorCode, index_directory);
  975.      else if (doc->ChunkCode == CT_byte)
  976.        element = (void*)getData(doc, &errorCode, index_directory);
  977.  
  978.      if (errorCode != GDT_NoError)
  979.        {            /* make a diagnostic record to return */
  980.      switch (errorCode)
  981.        { case GDT_UnsupportedChunkType:
  982.            diag = makeDiag(true,D_PresentRequestOutOfRange,
  983.                    "Bad ChunkType in Request");
  984.            break;
  985.          case GDT_BadDocID:
  986.            diag = makeDiag(true,D_PresentRequestOutOfRange,
  987.                    "Bad DocID in request");
  988.            break;
  989.          case GDT_MissingDocID:
  990.            diag = makeDiag(true,D_PresentRequestOutOfRange,
  991.                    "Missing DocID in request");
  992.            break;
  993.          case GDT_BadRange:
  994.            diag = makeDiag(true,D_PresentRequestOutOfRange,
  995.                    "Request out of range");
  996.            break;
  997.          case GDT_MissingDatabase:
  998.            diag = makeDiag(true,D_PresentRequestOutOfRange,
  999.                    "Database missing from request");
  1000.            break;
  1001.          case GDT_BadDatabase:
  1002.            diag = makeDiag(true,D_PresentRequestOutOfRange,
  1003.                    "File not present in specified database");
  1004.            break;
  1005.          default:
  1006.            /* should never get here */
  1007.            diag = NULL;
  1008.            break;
  1009.          };
  1010.      diags = (diagnosticRecord**)s_realloc(diags,(size_t)(sizeof(diagnosticRecord*) * 
  1011.                                   (numDiags + 2)));
  1012.      diags[numDiags++] = diag;
  1013.      diags[numDiags] = NULL;
  1014.        }
  1015.      if (element != NULL)
  1016.        { if (elementList == NULL) /* create a list */
  1017.        { elementList = (void**)s_malloc((size_t)sizeof(void*) * 2);
  1018.        }
  1019.        else            /* grow the list */
  1020.      { elementList = (void**)s_realloc((char*)elementList,
  1021.                        (size_t)(sizeof(void*) * 
  1022.                             (numElements + 2)));
  1023.      }
  1024.        elementList[numElements++] = element; /* put it in the list */
  1025.        elementList[numElements] = NULL;
  1026.        }
  1027.    }
  1028.  
  1029.   wais_response = makeWAISSearchResponse(NULL,NULL,NULL,NULL,
  1030.                                          (WAISDocumentText**)elementList,NULL,
  1031.                                          NULL,diags);
  1032.   response = makeSearchResponseAPDU(SUCCESS,0L,numElements + numDiags,0L,UNUSED,
  1033.                                     SUCCESS,aSearch->ReferenceID,
  1034.                     wais_response);
  1035.   
  1036.   /* write it */
  1037.   *sendBuf = writeSearchResponseAPDU(response,*sendBuf,sendBufLen);
  1038.   
  1039.   /* clean up */
  1040.   freeAny((any*)aSearch->Query); /* have to explicitly free the user info */
  1041.   freeSearchAPDU(aSearch);
  1042.   freeSearchResponseAPDU(response);
  1043.   freeWAISSearchResponse(wais_response); /* frees the elements constructed */
  1044.   doList((void**)docs,freeDocObj);
  1045.   s_free(docs);  
  1046. }
  1047.  
  1048. /*----------------------------------------------------------------------*/
  1049.  
  1050. static void
  1051. handleFTPSearch(aSearch,sendBuf,sendBufLen,
  1052.         waisProtocolVersion,
  1053.         index_directory)
  1054. SearchAPDU* aSearch;
  1055. char** sendBuf;
  1056. long* sendBufLen;
  1057. long waisProtocolVersion;
  1058. char *index_directory;
  1059. {
  1060.   SearchResponseAPDU* response = NULL;
  1061.   WAISSearchResponse* wais_response = NULL;
  1062.  
  1063.   DocID *t;
  1064.   WAISDocumentHeader** headers = NULL;
  1065.   long headerNum = 0;
  1066.   long max_headers = ((WAISSearch *)aSearch->Query)->MaxDocumentsRetrieved;
  1067.   DocObj *doc;
  1068.   char* seedwords_used = NULL;
  1069.   diagnosticRecord** diags = NULL;
  1070.   char *seed_words_used = s_strdup(((WAISSearch *)aSearch->Query)->SeedWords);
  1071.   char *database_name = (aSearch->DatabaseNames == NULL) ?
  1072.     INFO_DATABASE_NAME : aSearch->DatabaseNames[0];
  1073.   boolean search_status;
  1074.   int i=0;
  1075.   char *local_id,*p;
  1076.   long start,end;
  1077.   char path[200];
  1078. /*  char mpath[200]; Size is now assumed in getwd() */
  1079.   char mpath[MAX_FILENAME_LEN];
  1080.   int count;
  1081.  
  1082.   /* construct a response list */
  1083.   headers = (WAISDocumentHeader**)
  1084.     s_malloc((size_t)
  1085.          (sizeof(WAISDocumentHeader*) *
  1086.           (1 + max_headers)));
  1087.   headers[0] = NULL;
  1088.   for(doc=((WAISSearch *)aSearch->Query)->Docs[i]; ((WAISSearch *)aSearch->Query)->Docs[i]!=NULL;
  1089.       doc=((WAISSearch *)aSearch->Query)->Docs[++i]){
  1090.  
  1091.     t=docIDFromAny(doc->DocumentID);
  1092.     local_id = anyToString(GetLocalID(t));
  1093.     freeDocID(t);
  1094.     sscanf(local_id,"%ld %ld %s",&start,&end,path);
  1095.     if(strcmp(path, "/")) {
  1096.       p=strrchr(path,'/');
  1097.       if(p)
  1098.     *p='\0';
  1099.     }
  1100. /*    getwd(mpath,198); now second arg is assumed. andyj */
  1101.     getwd(mpath);
  1102.     if(index_directory && !substrcmp(path, index_directory))
  1103.       strcpy(path, index_directory);
  1104.  
  1105.     chdir(path);
  1106.  
  1107.     loadFileHeaders(path,headers,&headerNum,database_name, 
  1108.             ((WAISSearch *)aSearch->Query)->MaxDocumentsRetrieved,
  1109.             index_directory); 
  1110.     chdir(mpath);
  1111.   }
  1112.   /**** jim jim jim */
  1113.   wais_response = makeWAISSearchResponse(seedwords_used,headers,NULL,
  1114.                      NULL,NULL,NULL,NULL,diags);
  1115.   response = makeSearchResponseAPDU(search_status,0L,
  1116.                     headerNum + ((diags == NULL) ? 0 : 1),
  1117.                     0L,UNUSED,SUCCESS,
  1118.                     aSearch->ReferenceID, wais_response);
  1119.   /* write it */
  1120.   {
  1121.     char *buff;
  1122.     long len;
  1123.     boolean it_fit = true;
  1124.  
  1125.     while(headerNum >= 0) {
  1126.       buff = *sendBuf;
  1127.       len = *sendBufLen;
  1128.       if (headerNum*(sizeof(WAISDocumentHeader)+100) > len || (buff = writeSearchResponseAPDU(response,buff,&len)) == NULL) {
  1129.  
  1130.     it_fit = false;        /* didn't make it in the buffer. */
  1131.     headerNum--;
  1132.     s_free(headers[headerNum]);
  1133.     headers[headerNum] = NULL;
  1134.  
  1135.     s_free(wais_response);
  1136.     wais_response = makeWAISSearchResponse(seedwords_used,headers,NULL,
  1137.                            NULL,NULL,NULL,NULL,diags);
  1138.  
  1139.     freeSearchResponseAPDU(response);
  1140.     response = makeSearchResponseAPDU(search_status,0L,
  1141.                       headerNum + ((diags == NULL) ? 0:1),
  1142.                       0L,UNUSED,SUCCESS,
  1143.                       aSearch->ReferenceID, wais_response);
  1144.       }
  1145.       else {
  1146.     break;
  1147.       }
  1148.     }
  1149.     *sendBuf = buff;
  1150.     *sendBufLen = len;
  1151.     if (!it_fit) {
  1152.       waislog(WLOG_HIGH, WLOG_WARNING,
  1153.  
  1154.           "Buffer overflow, adjusted results from %ld",
  1155.           ((WAISSearch *)aSearch->Query)->MaxDocumentsRetrieved);
  1156.     }
  1157.   }
  1158.   {                /* generate report on results. */
  1159.     char *message;
  1160.     long size, i;
  1161.  
  1162.     /* calculate total length needed for log report */
  1163.     for(size = 0L, i = 0; i < headerNum; i++)
  1164.       size+=(headers[i]->DocumentID->size+2);
  1165.     if (size > 0) {
  1166.       message = s_malloc(size);
  1167.       message[0] = 0;
  1168.  
  1169.       for (i = 0; i < headerNum; i++) {
  1170.     char docname[MAX_FILE_NAME_LEN+50];
  1171.     DocID *docid = docIDFromAny(headers[i]->DocumentID);
  1172.     char *docidstring = anyToString(GetLocalID(docid));
  1173.  
  1174.     sprintf(docname, "%s", docidstring);
  1175.     s_strncat(message, docname, headers[i]->DocumentID->size, size);
  1176.  
  1177.     s_free(docid); 
  1178.     s_free(docidstring);
  1179.  
  1180.     if ( i < headerNum-1)
  1181.       strcat(message, ", ");
  1182.       }
  1183.       waislog(WLOG_LOW, WLOG_RESULTS,
  1184.           "Returned %d results: %s", headerNum, message);
  1185.       s_free(message);
  1186.     }
  1187.     else
  1188.       waislog(WLOG_LOW, WLOG_RESULTS,
  1189.           "Returned 0 results.  Aww.");
  1190.   }
  1191.   freeWAISSearch((WAISSearch*)aSearch->Query);
  1192.   freeSearchAPDU(aSearch);
  1193.   freeSearchResponseAPDU(response);
  1194.   freeWAISSearchResponse(wais_response); /* free headers & seed_words_used */
  1195. }
  1196.  
  1197. static int
  1198.   alphasort(d1, d2)
  1199. struct dirent **d1;
  1200. struct dirent **d2;
  1201. {
  1202.   return strcmp((*d1)->d_name, (*d2)->d_name);
  1203. }
  1204.  
  1205. static int
  1206. filesonly(e)
  1207. struct dirent *e;
  1208. {
  1209.   struct stat sb;
  1210.   int val;
  1211.   val = (stat(e->d_name, &sb) >= 0 &&( (sb.st_mode & S_IFMT) == S_IFREG || (sb.st_mode & S_IFMT)==S_IFDIR));
  1212.   if((sb.st_mode & S_IFMT)==S_IFDIR){
  1213.     if(e->d_name[strlen(e->d_name)-1]=='.')
  1214.       return(0);
  1215.     strcat(e->d_name,"/");
  1216.   }
  1217.   return(val);
  1218. }
  1219.  
  1220. loadFileHeaders(path,headers, headerNum,database_name,maxf,index_directory)
  1221. char *path;
  1222. WAISDocumentHeader **headers;
  1223. long *headerNum;
  1224. char *database_name;
  1225. long maxf;
  1226. char *index_directory;
  1227. {
  1228.   register int i;
  1229.   register int j;
  1230.   int k;
  1231.   struct dirent **list;
  1232.   hit help;
  1233.   char local_id[MAX_FILENAME_LEN + 60];
  1234.   DocID* theDocID = NULL;
  1235.   struct stat sbuf;
  1236.   long length,flen;
  1237.   long lines;
  1238.   char **type = NULL;
  1239.   char *p,tmpb[200];
  1240.   int loop;
  1241.   int ch,text=1;
  1242.   FILE *fp;
  1243.  
  1244.   k = *headerNum;
  1245.   if ((i = scandir(".", &list, filesonly, alphasort)) < 0) {
  1246.     return;
  1247.   }
  1248.   if(strcmp(path, "/") &&
  1249.      (index_directory == NULL || 
  1250.       strcmp(path, index_directory))) {
  1251.     pathname_directory(path, help.filename);
  1252.     stat(help.filename,&sbuf);
  1253.     length=lines=sbuf.st_size;
  1254.     strncpy(help.headline, help.filename, MAX_FILENAME_LEN);
  1255.     sprintf(local_id, "%ld %ld %s", 0L, length, help.filename);
  1256.     theDocID = makeDocID();
  1257.     theDocID->originalDatabase = stringToAny(database_name); /* XXX */
  1258.     theDocID->originalLocalID = stringToAny(local_id);
  1259.     type = (char**)s_malloc((size_t)(sizeof(char*) * 2));
  1260.     strcpy(tmpb,help.filename);
  1261.     type[0] = s_strdup("FTP-DIR");
  1262.     type[1] = NULL;
  1263.  
  1264.     headers[k++]=makeWAISDocumentHeader(anyFromDocID(theDocID),
  1265.                     UNUSED, MAX_NORMAL_SCORE, UNUSED, length,lines, type,
  1266.                     s_strdup(database_name), /* XXX */
  1267.                     NULL, /* date */
  1268.                     s_strdup(help.headline),
  1269.                     NULL);
  1270.   }
  1271.  
  1272.   if(!strcmp(path, "/")) {
  1273.     *path = '\0';
  1274.   }
  1275.   for (j = 0; j < i; j++){
  1276.     if(k>=maxf)
  1277.       break;
  1278.     sprintf(help.filename,"%s/%s",path,list[j]->d_name);
  1279.     stat(list[j]->d_name,&sbuf);
  1280.     length=lines=sbuf.st_size;
  1281.     strncpy(help.headline, help.filename, MAX_FILENAME_LEN);
  1282.     sprintf(local_id, "%ld %ld %s", 0L, length, help.filename);
  1283.     theDocID = makeDocID();
  1284.     theDocID->originalDatabase = stringToAny(database_name); /* XXX */
  1285.     theDocID->originalLocalID = stringToAny(local_id);
  1286.     type = (char**)s_malloc((size_t)(sizeof(char*) * 2));
  1287.     strcpy(tmpb,help.filename);
  1288.     p=strrchr(tmpb,'.');
  1289.     if(p){
  1290.       ++p;
  1291.       if(!strcasecmp(p,"tar") || !strcasecmp(p,"shar"))
  1292.     text=0;
  1293.       else
  1294.     text=1;
  1295.     }
  1296.     if(text==1){
  1297.       fp=fopen(help.filename,"r");
  1298.       if(fp==NULL)
  1299.     text=0;
  1300.       else{
  1301.     fseek(fp,0L,2);
  1302.     flen=ftell(fp);
  1303.     for(loop=2; loop<100; loop++){
  1304.       fseek(fp,flen/loop,0);
  1305.       ch=fgetc(fp);
  1306.       if(ch==EOF || (!isprint(ch) && !isspace(ch)))
  1307.         text=0;
  1308.     }
  1309.     fclose(fp);
  1310.       }
  1311.     }
  1312.  
  1313.     if(S_ISDIR(sbuf.st_mode))
  1314.       type[0] = s_strdup("FTP-DIR");
  1315.     else if(text==0)
  1316.       type[0] = s_strdup("FTP");
  1317.     else
  1318.       type[0]=s_strdup("TEXT");
  1319.  
  1320.     type[1] = NULL;
  1321.  
  1322.     headers[k++]=makeWAISDocumentHeader(anyFromDocID(theDocID),
  1323.                     UNUSED, MAX_NORMAL_SCORE, 
  1324.                     UNUSED, length,lines, type,
  1325.                     s_strdup(database_name), /* XXX */
  1326.                     NULL, /* date */
  1327.                     s_strdup(help.headline),
  1328.                     NULL);
  1329.   }
  1330.   if(list != NULL) {
  1331.     for (j = 0; j < i; j++)
  1332.       if(list[j] != NULL) free((char *)list[j]);
  1333.     free((char *)list);
  1334.   }
  1335.   *headerNum = k;
  1336. }
  1337.